/**
 * @Time: 2022/4/10 17:05
 * @Author: yt.yin
 * 描述: GO端常用辅助函数类
 */
package helper

import (
	"encoding/json"
	"errors"
	"io"
	"maps"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
	"golang.org/x/crypto/bcrypt"
	"learning.com/edu/utils/admin"
	"learning.com/edu/utils/jsonlog"
	"learning.com/edu/utils/validator"
)


type Helper struct {
	// 随机数
	rand *rand.Rand
	Logger *jsonlog.Logger
	Admin *admin.Admin
}

/** 创建助手类 */
func New(logger *jsonlog.Logger) *Helper {
	// 创建随机数
	rand := rand.New(rand.NewSource(time.Now().UnixNano()))
	// 创建助手
	return &Helper{
		Logger: logger,
		rand: rand,
		Admin: admin.New(logger),
	}
}

/** 获取ID参数 */
func (h *Helper) ReadIDParam(r *http.Request) (int64, error) {
	param := httprouter.ParamsFromContext(r.Context())
	id, err := strconv.ParseInt(param.ByName("id"), 10, 64)
	if err != nil || id < 1 {
		return id, errors.New("invalid id parameter")
	} else {
		return id, nil
	}
}

/** 获取JSON参数 */
func (h *Helper) ReadJSON(w http.ResponseWriter, r *http.Request, dst interface{}) error { 
	// 创建一个最大字节为1MB的io.LimitReader
	maxBytes := 1_048_576
	// 创建一个io.LimitReader
	r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
	// 创建一个JSON解码器
	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()
	// 解码JSON数据
	err := dec.Decode(dst)
	if err != nil {
		var syntaxError *json.SyntaxError
		var unmarshalTypeError *json.UnmarshalTypeError
		var invalidUnmarshalError *json.InvalidUnmarshalError

		var maxBytesError *http.MaxBytesError

		switch {
			// 语法错误
			case errors.As(err, &syntaxError):
				return errors.New("body contains badly-formed JSON (at character " + strconv.Itoa(int(syntaxError.Offset)) + ")")
			// 意外的EOF错误
			case errors.Is(err, io.ErrUnexpectedEOF):
				return errors.New("body contains badly-formed JSON")
			// 类型不匹配错误（UnmarshalTypeError）
			case errors.As(err, &unmarshalTypeError):
				if unmarshalTypeError.Field != "" {
					return errors.New("body contains incorrect JSON type for field" + unmarshalTypeError.Field)
				}
				return errors.New("body contains incorrect JSON type (at character " + strconv.Itoa(int(unmarshalTypeError.Offset)) + ")")
			// 空请求体
			case errors.Is(err, io.EOF):
				return errors.New("body must not be empty")
			// 未知字段错误
			case strings.HasPrefix(err.Error(), "json: unknown field"):
				fieldName := strings.TrimPrefix(err.Error(), "json: unknown field")
				return errors.New("body contains unknown key " + fieldName)
			// 错误请求体大小 
			case errors.As(err, &maxBytesError):
				return errors.New("body must not be larger than " + strconv.FormatInt(maxBytesError.Limit, 10) + " bytes")
			// 无效的解码
			case errors.As(err, &invalidUnmarshalError):
				panic(err)
			default:
				return err
		}
	}
	
	//  确保只有一个JSON值
	err = dec.Decode(&struct{}{})
	if err != io.EOF {
		return errors.New("body must only contain a single JSON value")
	}
	return nil
}

// 创建一个结构体
type Response map[string]interface {}

/**
 * 创建一个响应结构体
 * @param code 状态码
 * @param data 数据
 * @return 响应结构体
 */
func (h *Helper) NewResponse(code int, data map[string]interface{}) Response {
	response := Response(data)
	response["code"] = code
	return response
}

/**
 * 响应JSON数据
 * @param w http.ResponseWriter
 * @param status 状态码
 * @param data 数据
 * @param headers http.Header
 */
func (helper *Helper) WriteJSON(w http.ResponseWriter, status int, data Response, headers http.Header) error {
	// 响应JSON数据
	js, err := json.Marshal(data)
	if err != nil {
		return err
	}

	// 添加换行符
	js = append(js, '\n')

	// 添加HTTP头
	maps.Copy(w.Header(), headers)

	// 设置HTTP头
	w.Header().Set("Content-Type", "application/json")
	// 设置状态码
	w.WriteHeader(status)
	// 响应JSON数据
	w.Write(js)

	return nil
}

/**
 * 响应JSON数据string
 * @param w http.ResponseWriter
 */
func (h *Helper) ReadString(qs url.Values, key, defaultValue string) string {
	s := qs.Get(key)

	if s == "" {
		return defaultValue
	}
	return s
}

/**
 * 响应JSON数据csv[]
 * @param w http.ResponseWriter
 */
func (h *Helper) ReadCSV(qs url.Values, key string, defaultValue []string) []string {
	csv := qs.Get(key)

	if len(csv) == 0 {
		return defaultValue
	}

	return strings.Split(csv, ",")
}

/**
 * 响应JSON数据int
 * @param w http.ResponseWriter
 */
 func (h *Helper) ReadInt(qs url.Values, key string, defaultValue int, v *validator.Validator) int {
	s := qs.Get(key)

	if s == "" {
		return defaultValue
	}

	i, err := strconv.Atoi(s)
	if err != nil {
		v.AddError(key, "Must be an integer")
		return defaultValue
	}

	return i
}

/**
 * 响应JSON数据int64
 * @param w http.ResponseWriter
 */
 func (h *Helper) ReadInt64(qs url.Values, key string, defaultValue int64, v *validator.Validator) int64 {
	s := qs.Get(key)

	if s == "" {
		return defaultValue
	}

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		v.AddError(key, "Must be an integer")
		return defaultValue
	}
	return i
 }

 /**
 * 响应JSON数据bool
 * @param w http.ResponseWriter
 */
 func (h *Helper) ReadBool(qs url.Values, key string, defaultValue bool, v *validator.Validator) bool {
	s := qs.Get(key)

	if s == "" {
		return defaultValue
	}

	b, err := strconv.ParseBool(s)
	if err != nil {
		v.AddError(key, "Must be a boolean")
		return defaultValue
	}
	return b
 }

 /**
 * 响应JSON数据Int64Slice
 * @param w http.ResponseWriter
 */
 func (h *Helper) ReadInt64Slice(qs url.Values, key string, v *validator.Validator) []int64 {
	// 获取参数
	parts, ok := qs[key]
	if !ok || len(parts) == 0 {
		return nil
	}

	// 循环转换
	ints := make([]int64, 0, len(parts))
	for _, s := range parts {
		i, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			v.AddError(key, "Must be a comma-separated list of integers")
			return []int64{}
		}
		ints = append(ints, i)
	}
	return ints
 }

 /**
  * 验证密码
  * @param key 参数名称
  * @param defaultValue 默认值
  * @return string
  */
 func (h *Helper) GeneratePasswordHash(password string) ([]byte, error) {
	return bcrypt.GenerateFromPassword([]byte(password), 12)
 }

 func (helper *Helper) ComparePasswordAndHash(password string, hash []byte) (bool, error) {
	err := bcrypt.CompareHashAndPassword(hash, []byte(password))
	if err != nil {
		if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
			return false, nil
		}
		return false, err
	} else {
		return true, nil
	}
}
 
 /**
  * 生成随机字符串
  * @param length 字符串长度
  * @return string
  */
 func (h *Helper) RandomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[h.rand.Intn(len(charset))]
	}
	return string(b)
}